package com.kang.c0c.symbol;

import java.util.Hashtable;
import java.util.Vector;

import com.kang.c0c.parser.Token;
import com.kang.c0c.util.Log;

/**
 * 符号表又称名字特性表，是存储出现在源程序代码中的所有用户自定义标识符（主要包括三类
 *	变量标识符		：Symbol
 * 	常量标识符		：ConstantSymbol
 * 	自定义函数标识符	：FunctionSymbol）
 * 		类Symbol同时也是类ConstantSymbol和类FunctionSymbol的父类
 * 
 * NameTable是一类普通的符号表：它以javaBean的形式存在，不区分全局与局部
 * 一个NameTable只存储一个相关局部（全局是一种特殊的局部）的符号表信息
 * 
 * NameTable存储符号表的数据结构采用了Hashtable（Hashtable<String,Symbol> table），其键值（key）为标识符的名称，值（value）为所要存储的符号Symbol
 * 由于C0文法要求标识符对大小写不敏感，所以NameTable在存储和索引键值时都会将key值全部转化为小写字母
 * 另外在生成目标代码时需要依序地查找出符号表中的符号项信息，所以以向量的形式将所有哈希表中的键值存储在 Vector<String> keys中
 * 
 * NameTable在以javaBean的形式存在的同时，对外提供了操作NameTable存储三类四种符号标识符的静态方法：
 * 	insertVar		：在指定NameTable中插入变量标识符的标识符记号的符号信息
 * 	insertConst		：在指定NameTable中插入常量标识符的标识符记号和值记号的符号信息
 * 	insertFunc		：在指定NameTable中插入函数标识符的标识符记号的符号信息，
 * 					  并返回中间生成的FunctionSymbol，这一项是在com.kang.c0c.parser.Parser进行语法分析生成此函数自己的局部符号表时所需要的
 * 	insertParam		：在指定NameTable中插入函数参数标识符记号的符号信息
 *
 * @author kang
 *
 * Feb 7, 2009
 */
public class NameTable {
	/*
	 * 以下为NameTable对外提供的操作NameTable存储三类四种符号标识符的静态方法
	 */
	public static void insertVar(NameTable table,Token token, Log log){
		Symbol sym = new Symbol(token.getPos(),(String)token.getValue(),Kinds.VAR);
		if(table.get(sym.getName()) == null){
			if(!table.put(sym.getName(), sym)){
				log.error(token.getPos(), "Fatal Mistake : Cann't put the variable identifier '"
						+ token.getValue() + "' into the symbol table");
				log.tips("It's made by the designer of the compiler, please contact with him.\n"
						+"\tkangcwt@gmail.com\n"
						+"\tThanks for your help!");
			}else
				table.addVarCount();
				
		}else
			log.error(token.getPos(), "Duplicate local variable identifier '" + token.getValue() + "'");
	}
	
	public static Symbol insertParam(NameTable table,Token token, Log log){
		Symbol sym = new Symbol(token.getPos(),(String)token.getValue(),Kinds.PARAM);
		if(table.get(sym.getName()) == null){
			if(!table.put(sym.getName(), sym)){
				log.error(token.getPos(), "Fatal Mistake : Cann't put the parameter identifier '"
						+ token.getValue() + "' into the symbol table");
				log.tips("It's made by the designer of the compiler, please contact with him.\n"
						+"\tkangcwt@gmail.com\n"
						+"\tThanks for your help!");
			}
		}else
			log.error(token.getPos(), "Duplicate local parameter identifier '" + token.getValue() + "'");
		
		return sym;
	}
	
	public static void insertConst(NameTable table,Token ident, Token value, Log log){
		ConstantSymbol sym = new ConstantSymbol(ident.getPos(), 
				(String)ident.getValue(), (Integer)value.getValue());
		
		if(table.get(sym.getName()) == null){
			if(! table.put(sym.getName(), sym)){
				log.error(ident.getPos(), "Fatal Mistake : Cann't put the const identifier '"
						+ ident.getValue() + "' into the symbol table");
				log.tips("It's made by the designer of the compiler, please contact with him.\n"
						+"\tkangcwt@gmail.com\n"
						+"\tThanks for your help!");
			}
		}else
			log.error(ident.getPos(), "Duplicate local const identifier '" + ident.getValue() + "'");		
	}
	
	public static FunctionSymbol insertFunc(NameTable table,Token token, int type, Log log){
		FunctionSymbol sym = new FunctionSymbol(table, token.getPos(),
				(String)token.getValue(), type);
		if(table.get(sym.getName()) == null){
			if(! table.put(sym.getName(), sym)){
				log.error(token.getPos(), "Fatal Mistake : Cann't put the function identifier '"
						+ token.getValue() + "' into the symbol table");
				log.tips("It's made by the designer of the compiler, please contact with him.\n"
						+"\tkangcwt@gmail.com\n"
						+"\tThanks for your help!");
			}else
				table.addFuncCount();
		}else
			log.error(token.getPos(), "Duplicate local function identifier '" + token.getValue() + "'");
		
		return sym;
	}
	
	private Hashtable<String,Symbol> table;
	private Vector<String> keys;
	private NameTable father;
	
	/*
	 * varCount			:符号表中变量的个数
	 * funcCount		:符号表中用户自定义函数的个数
	 */
	private int varCount;
	private int funcCount;
	
	public NameTable(NameTable father){
		this.father = father;
		
		table = new Hashtable<String,Symbol>();
		varCount = 0;
		funcCount = 0;
		keys = new Vector<String>();
	}
	
	public boolean put(String key,Symbol sym){
		if(table.get(key.toLowerCase()) == null){
			table.put(key.toLowerCase(), sym);
			keys.add(key);
			return true;
		}else
			return false;
	}
	
	public Symbol get(String key){
		return table.get(key.toLowerCase());
	}
	
	public String getKey(int i){
		return keys.elementAt(i);
	}
	
	public Symbol symbolAt(int i){
		return get(getKey(i));
	}
	
	public void remove(int index){
		String key = keys.elementAt(index);
		keys.remove(index);
		table.remove(key);
	}
	
	public int size(){
		return table.size();
	}
	
	public int getVarCount() {
		return varCount;
	}

	public void addVarCount() {
		this.varCount ++;
	}
	
	public int getFuncCount() {
		return funcCount;
	}

	public void addFuncCount() {
		this.funcCount ++;
	}
	
	public NameTable getFather() {
		return father;
	}
}
